import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_deer/net/http_api.dart';
import 'package:flutter_deer/res/constant.dart';
import 'package:flutter_deer/util/log_utils.dart';
import 'base_entity.dart';
import 'error_handle.dart';
import 'intercept.dart';

/// 默认dio配置
/// 连接超时时间，默认15秒
Duration _connectTimeout = const Duration(seconds: 15);

/// 接收超时时间，默认15秒
Duration _receiveTimeout = const Duration(seconds: 15);

/// 发送超时时间，默认10秒
Duration _sendTimeout = const Duration(seconds: 10);

/// 请求的基础URL，默认为空字符串
String _baseUrl = '';

/// 拦截器列表，默认为空列表
List<Interceptor> _interceptors = [];

/// 初始化Dio配置
/// [connectTimeout] 连接超时时间，可选参数
/// [receiveTimeout] 接收超时时间，可选参数
/// [sendTimeout] 发送超时时间，可选参数
/// [baseUrl] 请求的基础URL，可选参数
/// [interceptors] 拦截器列表，可选参数
void configDio({
  Duration? connectTimeout,
  Duration? receiveTimeout,
  Duration? sendTimeout,
  String? baseUrl,
  List<Interceptor>? interceptors,
}) {
  // 若传入了新的连接超时时间，则更新，否则保持原有值
  _connectTimeout = connectTimeout ?? _connectTimeout;
  // 若传入了新的接收超时时间，则更新，否则保持原有值
  _receiveTimeout = receiveTimeout ?? _receiveTimeout;
  // 若传入了新的发送超时时间，则更新，否则保持原有值
  _sendTimeout = sendTimeout ?? _sendTimeout;
  // 若传入了新的基础URL，则更新，否则保持原有值
  _baseUrl = baseUrl ?? _baseUrl;
  // 若传入了新的拦截器列表，则更新，否则保持原有值
  _interceptors = interceptors ?? _interceptors;
}

/// 网络请求成功的回调，接收泛型数据
typedef NetSuccessCallback<T> = void Function(T data);

/// 网络请求成功返回列表数据的回调，接收泛型列表数据
typedef NetSuccessListCallback<T> = void Function(List<T> data);

/// 网络请求失败的回调，接收错误码和错误信息
typedef NetErrorCallback = void Function(int code, String msg);

/// @weilu https://github.com/simplezhli
class DioUtils {
  /// 获取单例实例
  factory DioUtils() => _singleton;

  /// 私有构造函数，初始化Dio实例
  DioUtils._() {
    final BaseOptions options = BaseOptions(
      connectTimeout: _connectTimeout,
      receiveTimeout: _receiveTimeout,
      sendTimeout: _sendTimeout,

      /// dio默认json解析，这里指定返回UTF8字符串，自己处理解析。（可也以自定义Transformer实现）
      responseType: ResponseType.plain,
      validateStatus: (_) {
        // 不使用http状态码判断状态，使用AdapterInterceptor来处理（适用于标准REST风格）
        return true;
      },
      baseUrl: _baseUrl,
//      contentType: Headers.formUrlEncodedContentType, // 适用于post form表单提交
    );
    // 初始化Dio实例
    _dio = Dio(options);
    _dioGbPrinter = Dio(BaseOptions(
      connectTimeout: _connectTimeout,
      receiveTimeout: _receiveTimeout,
      sendTimeout: _sendTimeout,
      /// dio默认json解析，这里指定返回UTF8字符串，自己处理解析。（可也以自定义Transformer实现）
      responseType: ResponseType.plain,
      validateStatus: (_) {
        // 不使用http状态码判断状态，使用AdapterInterceptor来处理（适用于标准REST风格）
        return true;
      },
      baseUrl: HttpApi.BaseUrlGbPrinter,
      contentType: Headers.formUrlEncodedContentType, // 适用于post form表单提交
    ));

    /// Fiddler抓包代理配置 https://www.jianshu.com/p/d831b1f7c45b
    // _dio.httpClientAdapter = IOHttpClientAdapter()..onHttpClientCreate = (HttpClient client) {
    //   client.findProxy = (uri) {
    //     //proxy all request to localhost:8888
    //     return 'PROXY 10.41.0.132:8888';
    //   };
    //   return client;
    // };

    /// 添加拦截器的方法
    void addInterceptor(Interceptor interceptor) {
      // 向Dio实例中添加拦截器
      _dio.interceptors.add(interceptor);
    }

    _dioGbPrinter.interceptors.add(LoggingInterceptor());

    // 遍历拦截器列表并添加到Dio实例中
    _interceptors.forEach(addInterceptor);
  }

  /// 单例实例
  static final DioUtils _singleton = DioUtils._();

  /// 获取单例实例的便捷方法
  static DioUtils get instance => DioUtils();

  /// Dio实例
  static late Dio _dio;

  /// 获取Dio实例
  Dio get dio => _dio;

  /// Dio_jb
  static late Dio _dioGbPrinter;

  /// 获取Dio_jb实例
  Dio get dioGbPrinter => _dioGbPrinter;

  // 数据返回格式统一，统一处理异常
  /// 发起网络请求并返回BaseEntity对象
  /// [method] 请求方法
  /// [url] 请求的URL
  /// [data] 请求体数据，可选参数
  /// [queryParameters] 查询参数，可选参数
  /// [cancelToken] 取消请求的令牌，可选参数
  /// [options] 请求选项，可选参数
  Future<BaseEntity<T>> _request<T>(
    String method,
    String url, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    Options? options,
  }) async {
    try {
      final Response<String> response = await _dio.request<String>(
        url,
        data: data,
        queryParameters: queryParameters,
        options: _checkOptions(method, options),
        cancelToken: cancelToken,
      );

      final String? rawData = response.data;
      if (rawData == null || rawData.isEmpty) {
        Log.e('接口返回空数据', customTag: 'Network');
        return ExceptionHandle.handleEmptyResponse<T>();
      }

      final bool isCompute = !Constant.isDriverTest && rawData.length > 10 * 1024;
      late Map<String, dynamic> map;

      try {
        map = isCompute ? await compute(parseData, rawData) : parseData(rawData);
      } catch (e, stackTrace) {
        Log.e('数据解析失败: $e\n$stackTrace', customTag: 'ParseError');
        return ExceptionHandle.handleParseError<T>(rawData);
      }

      return BaseEntity<T>.fromJson(map);
    } on DioException catch (e) {
      return ExceptionHandle.handleDioException<T>(e);
    } catch (e) {
      Log.e('未知异常: $e', customTag: 'Unknown');
      return ExceptionHandle.handleUnknownException<T>('未知异常');
    }
  }

  Future<BaseGbEntity<T>> _requestGbPrinter<T>(
      String method,
      String url, {
        Object? data,
        Map<String, dynamic>? queryParameters,
        CancelToken? cancelToken,
        Options? options,
      }) async {
    try {
      final Response<String> response = await _dioGbPrinter.request<String>(
        url,
        data: data,
        queryParameters: queryParameters,
        options: _checkOptions(method, options),
        cancelToken: cancelToken,
      );

      final String? rawData = response.data;
      if (rawData == null || rawData.isEmpty) {
        Log.e('接口返回空数据', customTag: 'Network');
        return BaseGbEntity<T>(ExceptionHandle.empty_response, '接口返回空数据', null);
      }

      final bool isCompute = !Constant.isDriverTest && rawData.length > 10 * 1024;
      late Map<String, dynamic> map;

      try {
        map = isCompute ? await compute(parseData, rawData) : parseData(rawData);
      } catch (e, stackTrace) {
        print('数据解析失败: $e\n$stackTrace');
        return BaseGbEntity<T>(ExceptionHandle.parse_error, '数据解析失败，请重试\n${rawData ?? ''}', null);
      }
      return BaseGbEntity<T>.fromJson(map);
    } on DioException catch (e) {
      return BaseGbEntity<T>(-1, '请求已取消', null);
    } catch (e) {
      Log.e('未知异常: $e', customTag: 'Unknown');
      return BaseGbEntity<T>(ExceptionHandle.unknown_error, '未知异常', null);
    }
  }

  /// 检查并设置请求选项
  /// [method] 请求方法
  /// [options] 请求选项，可选参数
  Options _checkOptions(String method, Options? options) {
    // 若未传入请求选项，则创建一个新的Options对象
    options ??= Options();
    // 设置请求方法
    options.method = method;
    return options;
  }

  /// 发起网络请求并返回Future对象
  /// [method] 请求方法
  /// [url] 请求的URL
  /// [onSuccess] 请求成功的回调，可选参数
  /// [onError] 请求失败的回调，可选参数
  /// [params] 请求体数据，可选参数
  /// [queryParameters] 查询参数，可选参数
  /// [cancelToken] 取消请求的令牌，可选参数
  /// [options] 请求选项，可选参数
  Future<dynamic> requestNetwork<T>(
    Method method,
    String url, {
    NetSuccessCallback<T?>? onSuccess,
    NetErrorCallback? onError,
    Object? params,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    Options? options,
  }) {
    return _request<T>(
      method.value,
      url,
      data: params,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
    ).then<void>((BaseEntity<T> result) {
      // 请求成功且返回码为0时，调用成功回调
      if (result.code == 0) {
        onSuccess?.call(result.data);
      } else {
        // 请求失败时，调用错误处理方法
        _onError(result.code, result.message, onError);
      }
    }, onError: (dynamic e) {
      // 请求异常时，打印取消请求日志
      _cancelLogPrint(e, url);
      // 处理异常并获取错误信息
      final BaseEntity<dynamic> error = ExceptionHandle.handleException(e);
      // 调用错误处理方法
      _onError(error.code, error.message, onError);
    });
  }
  Future<dynamic> requestNetworkGbPrinter<T>(
    Method method,
    String url, {
    NetSuccessCallback<T?>? onSuccess,
    NetErrorCallback? onError,
    Object? params,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    Options? options,
  }) {
    return _requestGbPrinter<T>(
      method.value,
      url,
      data: params,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
    ).then<void>((BaseGbEntity<T> result) {
      // 请求成功且返回码为0时，调用成功回调
      if (result.code == 0) {
        onSuccess?.call(result.data);
      } else {
        // 请求失败时，调用错误处理方法
        _onError(result.code, result.msg, onError);
      }
    }, onError: (dynamic e) {
      // 请求异常时，打印取消请求日志
      _cancelLogPrint(e, url);
      // 处理异常并获取错误信息
      final BaseEntity<dynamic> error = ExceptionHandle.handleException(e);
      // 调用错误处理方法
      _onError(error.code, error.message, onError);
    });
  }

  /// 统一处理(onSuccess返回T对象，onSuccessList返回 List<T>)
  /// 异步发起网络请求
  /// [method] 请求方法
  /// [url] 请求的URL
  /// [onSuccess] 请求成功的回调，可选参数
  /// [onError] 请求失败的回调，可选参数
  /// [params] 请求体数据，可选参数
  /// [queryParameters] 查询参数，可选参数
  /// [cancelToken] 取消请求的令牌，可选参数
  /// [options] 请求选项，可选参数
  void asyncRequestNetwork<T>(
    Method method,
    String url, {
    NetSuccessCallback<T?>? onSuccess,
    NetErrorCallback? onError,
    Object? params,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    Options? options,
  }) {
    // 将请求包装成Stream
    Stream.fromFuture(_request<T>(
      method.value,
      url,
      data: params,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
    )).asBroadcastStream().listen((result) {
      // 请求成功且返回码为0时，调用成功回调
      if (result.code == 0) {
        if (onSuccess != null) {
          onSuccess(result.data);
        }
      } else {
        // 请求失败时，调用错误处理方法
        _onError(result.code, result.message, onError);
      }
    }, onError: (dynamic e) {
      // 请求异常时，打印取消请求日志
      _cancelLogPrint(e, url);
      // 处理异常并获取错误信息
      final BaseEntity<dynamic> error = ExceptionHandle.handleException(e);
      // 调用错误处理方法
      _onError(error.code, error.message, onError);
    });
  }

  /// 打印取消请求的日志
  /// [e] 异常对象
  /// [url] 请求的URL
  void _cancelLogPrint(dynamic e, String url) {
    if (e is DioException && CancelToken.isCancel(e)) {
      // 若请求被取消，打印日志
      Log.e('取消请求接口： $url');
    }
  }

  /// 统一处理错误信息
  /// [code] 错误码，可选参数
  /// [msg] 错误信息
  /// [onError] 错误回调，可选参数
  void _onError(int? code, String msg, NetErrorCallback? onError) {
    if (code == null) {
      // 若错误码为空，设置默认错误码和错误信息
      code = ExceptionHandle.unknown_error;
      msg = '未知异常';
    }
    // 打印接口请求异常日志
    Log.e('接口请求异常： code: $code, msg: $msg');
    // 调用错误回调
    onError?.call(code, msg);
  }
}

/// 解析JSON字符串为Map对象
/// [data] JSON字符串
Map<String, dynamic> parseData(String data) {
  return json.decode(data) as Map<String, dynamic>;
}

/// 网络请求方法枚举
enum Method { get, post, put, patch, delete, head }

/// 使用拓展枚举替代 switch判断取值
/// https://zhuanlan.zhihu.com/p/98545689
/// 方法枚举的拓展类
extension MethodExtension on Method {
  /// 获取枚举对应的HTTP请求方法字符串
  String get value => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'][index];
}
